import { Link } from '@brillout/docpress'
import { UiFrameworkExtension } from '../../components'

If you don't use <UiFrameworkExtension name />, then you have direct control over `<head>` tags in your <Link href="/onRenderHtml">`onRenderHtml()` hook</Link>.

> If you use <UiFrameworkExtension name />, then see <Link href="/head-tags" />.

```ts
// /renderer/+onRenderHtml.ts
// Environment: server

import type { PageContextServer } from 'vike/types'
import { escapeInject, dangerouslySkipEscape } from 'vike/server'
import { renderToHtml } from 'some-ui-framework'

export { onRenderHtml }

async function onRenderHtml(pageContext: PageContextServer) {
  return escapeInject`<html>
    <head>
      <title>SpaceX</title>
      <meta name="description" content="We deliver payload to space.">
    </head>
    <body>
      <div id="root">
        ${dangerouslySkipEscape(await renderToHtml(pageContext.Page))}
      </div>
    </body>
  </html>`
}
```


## By page

To define `<head>` tags on page-by-page basis, we recommend creating new settings, for example `title` and `description` as shown at <Link href="/meta#example-title-and-description" doNotInferSectionTitle />.


## By component

To define `<head>` tags by any UI component:
 1. Add `'headProps'` to [`passToClient`](/passToClient).
 2. Use <Link href="/usePageContext">`usePageContext()`</Link> so that `pageContext.headProps` can be accessed and modified by any component.

For example:

```tsx
// /renderer/+onRenderHtml.ts
// Environment: server

export { onRenderHtml }

import type { PageContextServer } from 'vike/types'
import { escapeInject, dangerouslySkipEscape } from 'vike/server'
import renderToHtml from 'some-ui-framework'

async function onRenderHtml(pageContext: PageContextServer) {
  // We use our UI framework to pass `pageContext.headProps` to all components
  // of our component tree. (E.g. React Context or Vue's `app.config.globalProperties`.)
  const pageHtml = await renderToHtml(
    <ContextProvider headProps={pageContext.headProps} >
      <Page />
    </ContextProvider>
  )

  // 1. One of our UI component modified pageContext.headProps while rendering components.
  // 2. We now render `headProps` to HTML meta tags.
  return escapeInject`<html>
    <head>
      <title>${pageContext.headProps.title}</title>
      <meta name="description" content="${pageContext.headProps.description}">
    </head>
    <body>
      <div id="app">
        ${dangerouslySkipEscape(pageHtml)}
      </div>
    </body>
  </html>`
}
```

```ts
// SomeComponent.ts

/* ... */

// Inside any UI component
const pageContext = usePageContext()
const { headProps } = pageContext
headProps.title = 'I was set by some component.'
headProps.description = 'Me too.'
```


## Client Routing

If you use Client Routing, make sure to update `document.title` upon page navigation:

```ts
// /renderer/+config.ts
// Environment: config

import type { Config } from 'vike/types'

export default {
  // Make pageContext.headProps available on the client-side.
  passToClient: ['headProps']
} satisfies Config
```

```ts
// /renderer/+onRenderClient.ts
// Environment: client

export { onRenderClient }

import type { PageContextClient } from 'vike/types'

async function onRenderClient(pageContext: PageContextClient) {
  if (!pageContext.isHydration) {
    // Client-side navigation — we update the page's title
    document.title = pageContext.headProps.title
  }
  // ...
}
```


## Libraries

You can also use libraries such as [@vueuse/head](https://github.com/vueuse/head) or [react-helmet](https://github.com/nfl/react-helmet).

But we recommend using such library only if you have a rationale as the aforementioned solutions are simpler.

Head libraries already sanitize the HTML `<head>`, this means you can skip `escapeInject` and wrap the overall result with `dangerouslySkipEscape()`.

```ts
// /renderer/+onRenderHtml.ts
// Environment: server

export { onRenderHtml }

import type { PageContextServer } from 'vike/types'
import { dangerouslySkipEscape } from 'vike/server'
import { renderToHtml } from 'some-ui-framework'

async function onRenderHtml(pageContext: PageContextServer) {
  return dangerouslySkipEscape(await renderToHtml(pageContext.Page))
}
```

